package firing;

import firing.Node;
import game.Bullet;
import game.Enemy;
import game.Entity;
import game.Player;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;


/** Represents a complete decision tree for an AI deciding how to fire */
public class FiringTree implements java.io.Serializable, Comparable<FiringTree> {

    private static final long serialVersionUID = -5569222922263118110L;
    public Node base;
    private int heuristic = -1;
    private int generation = 1;
    
    /** determines angle that points at player, then executes decision tree */
    public void fire(Entity target, Enemy shooter, List<Bullet> bullets, Player player) {
	shooter.incrementLastFired();
	double slope;
	double angle;
	if (target.getX() - shooter.getX() == 0) {
	    if (target.getY() < shooter.getY()) {
		angle = Math.PI*3/2;
	    } else {
		angle = Math.PI/2;
	    }
	} else {
	    slope = ((double)target.getY() - shooter.getY()) / ((double)target.getX() - shooter.getX());
	    angle = Math.atan(slope);
		if (target.getX() < shooter.getX()) {
		    angle = Math.PI + angle;
		}
	}
	base.calculate(bullets, angle, shooter, player);
    }
    
    public FiringTree duplicate() {
	FiringTree result = new FiringTree();
	result.base = base.duplicate();
	result.generation = generation + 1;
	return result;
    }
    
    public Node getRandomNode() {
	List<Node> nodes = getNodesInTree();
	return nodes.get((int)(Math.random()*nodes.size()));
    }

    public int getNumberOfNodes() {
	return getNodesInTree().size();
    }
    
    /** Counts the number of children a node has */
    public static int getNumberOfChildren(Node n) {
	LinkedList<Node> queue = new LinkedList<Node>();
	ArrayList<Node> nodesInTree = new ArrayList<Node>();
	queue.add(n);
	while (!queue.isEmpty()) {
	    Node next = queue.remove();
	    if (next != null) {
		nodesInTree.add(next);
		if (next.isUnaryNode()) {
		    queue.add(((UnaryNode)next).getChild());
		} else {
		    queue.add(((BinaryNode)next).getLeftChild());
		    queue.add(((BinaryNode)next).getRightChild());
		}
	    }
	}
	return nodesInTree.size();
    }
    
    /** Gets a random node from the provided tree */
    public List<Node> getNodesInTree() {
	LinkedList<Node> queue = new LinkedList<Node>();
	ArrayList<Node> nodesInTree = new ArrayList<Node>();
	queue.add(base);
	while (!queue.isEmpty()) {
	    Node next = queue.remove();
	    if (next != null) {
		nodesInTree.add(next);
		if (next.isUnaryNode()) {
		    queue.add(((UnaryNode)next).getChild());
		} else {
		    queue.add(((BinaryNode)next).getLeftChild());
		    queue.add(((BinaryNode)next).getRightChild());
		}
	    }
	}
	return nodesInTree;
    }
    
    public int getHeuristic() {
	return heuristic;
    }
    
    public void setHeuristic(int i) {
	this.heuristic = i;
    }
    
    public int getGeneration() {
	return generation;
    }
    
    public String toString() {
	return "" + generation + "." + heuristic + ".ftree";
    }
    
    public String toString(int index) {
	return "" + generation + "." + index + "." + heuristic + ".ftree";
    }

    @Override
    public int compareTo(FiringTree other) {
	if (heuristic == other.heuristic) {
	    return 0;
	} else if (heuristic < other.heuristic){
	    return -1;
	} else {
	    return 1;
	}
    }
    
    public String printTree() {
	return "" + getNumberOfNodes() + base.printNode(0);
    }
    
}
